package com.example.autumn.service.impl;

import com.example.autumn.entity.AccountFreeze;
import com.example.autumn.mapper.AccountFreezeMapper;
import com.example.autumn.mapper.AccountMapper;
import com.example.autumn.service.IAccountTccService;
import io.seata.core.context.RootContext;
import io.seata.rm.tcc.api.BusinessActionContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;

/**
 * Seata 的 TCC 事务解决方案实现
 *
 * @author Liaorun
 */
@Service
public class IAccountTccServiceImpl implements IAccountTccService {

    @Autowired
    private AccountMapper accountMapper;

    @Autowired
    private AccountFreezeMapper accountFreezeMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deduct(String userId, int money) {
        // 0. 获取事务id
        String xid = RootContext.getXID();

        // a. 悬挂问题，在空回滚之后，try有正常执行了
        AccountFreeze accountFreeze = accountFreezeMapper.selectOne(new AccountFreeze().setXid(xid));

        if (accountFreeze != null) {
            return;
        }


        // 1. 扣减可以余额
        accountMapper.deduct(userId, money);
        // 2. 记录冻结余额，事务状态

        AccountFreeze freeze = new AccountFreeze();

        freeze.userId = userId;
        freeze.freezeMoney = new BigDecimal(money);
        freeze.state = AccountFreeze.State.Try;
        freeze.xid = xid;

        accountFreezeMapper.insert(freeze);
    }

    /**
     * 本次全局事务成功之后调用的方法
     *
     * @param context 上下文
     * @return 是否成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean commit(BusinessActionContext context) {
        // 1.获取事务id
        String xid = context.getXid();
        // 2.根据事务id删除冻结记录
        Integer count = accountFreezeMapper.deleteById(xid);
        return count == 1;
    }

    /**
     * 本次全局事务失败之后调用的方法
     * 需要支持空回滚，业务可能没有执行 try 方法，全局事务就失败了，这时候就会空回滚
     *
     * @param context 上下文
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean cancel(BusinessActionContext context) {
        // 0. 查询冻结记录
        AccountFreeze accountFreeze = accountFreezeMapper.selectOne(new AccountFreeze().setXid(context.getXid()));

        // a.空回滚问题
        // 需要支持空回滚，业务可能没有执行 try 方法，全局事务就失败了
        // accountFreeze 就会为空
        if (accountFreeze == null) {
            Object userId = context.getActionContext("userId");
            String userIdStr = String.valueOf(userId);

            accountFreeze = new AccountFreeze().setState(AccountFreeze.State.CANCEL).setXid(context.getXid())
                    .setUserId(userIdStr).setFreezeMoney(BigDecimal.valueOf(0));

            Integer count = accountFreezeMapper.insert(accountFreeze);
            return count == 1;
        }

        // b. 幂等问题，cancel 超时，这时候seata 重试cancel方法
        if (accountFreeze.state == AccountFreeze.State.CANCEL) {
            return true;
        }

        // 1. 恢复可用余额
        accountMapper.refund(accountFreeze.userId, accountFreeze.freezeMoney);
        // 2. 将冻结金额清零，状态改为cancle
        accountFreeze.setFreezeMoney(new BigDecimal(0));
        accountFreeze.setState(AccountFreeze.State.CANCEL);

        Integer count = accountFreezeMapper.updateById(accountFreeze);
        return count == 1;
    }
}
